package com.robertux.leaderboard.util.gestoresModelos;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.log4j.Logger;

import com.robertux.leaderboard.beans.ModalidadGrupoProducto;
import com.robertux.leaderboard.beans.entities.ExclusionCliente;
import com.robertux.leaderboard.beans.entities.GrupoProducto;
import com.robertux.leaderboard.beans.entities.Meta;
import com.robertux.leaderboard.beans.entities.external.ItemVenta;
import com.robertux.leaderboard.beans.entities.external.Producto;
import com.robertux.leaderboard.beans.entities.external.Sucursal;
import com.robertux.leaderboard.util.FarmaciaQueriesBuilder;
import com.robertux.leaderboard.util.ResourceLocator;

public class GestorEntidadesFarmacia {
	protected static final String FARMACIA_JNDI = "java:comp/env/jdbc/FarmaciaDB";
	protected Logger logger;
	private Connection conn;
	private Properties connectionProps;
	
	public GestorEntidadesFarmacia() {
		this.logger = Logger.getLogger(this.getClass());
	}
	
	public GestorEntidadesFarmacia(Properties connProps) {
		this();
		this.connectionProps = connProps;
	}
	
	public GestorEntidadesFarmacia(Connection conn) {
		this();
		this.conn = conn;
	}
	
	public Connection getConnection() {
		boolean isClosed = false;
		try {
			isClosed = (this.conn != null && this.conn.isClosed());
		} catch (Exception e) {
			this.logger.warn("Error tratando de validar si la conexión al SQL Server sigue activa: " + e.getMessage());
		}
		if(this.conn == null || isClosed) {
			
			this.logger.info("Cargando conexión con recurso Properties");
			if(this.conn == null && this.connectionProps != null && this.connectionProps.getProperty("javax.persistence.jdbc.driver") != null) {
				this.logger.info("Cargando conexión con archivo de propiedades");
				this.conn = this.getConnection(this.connectionProps);
			}
			
			if(this.conn == null) {
				this.logger.info("Cargando conexión con recurso JNDI");
				try {
					this.conn = ResourceLocator.dataSourceLookup(FARMACIA_JNDI);
				} catch (Exception e) {
					this.logger.error("Error tratando de cargar la conexión con el recurso JNDI " + FARMACIA_JNDI + ": " + e.getMessage());
				}
			}
		}
		return this.conn;
	}
	
	public Connection getConnection(Properties props) {
		try {
			Class.forName(props.getProperty("javax.persistence.jdbc.driver"));
			return DriverManager.getConnection(props.getProperty("javax.persistence.jdbc.url"), 
					props.getProperty("javax.persistence.jdbc.user"), 
					props.getProperty("javax.persistence.jdbc.password"));
			
		} catch (Exception e) {
			this.logger.error("Error tratando de crear una conexión al SQL Server: " + e.getMessage(), e);
			return null;
		}
	}
	
	public void closeConnection() {
		if(this.conn != null) {
			try { this.conn.close(); } catch (Exception e) { this.logger.warn("Error tratando de cerrar la conexion: " + e.getMessage()); }
		}
	}
	
	public List<Sucursal> getSucursales() {
		List<Sucursal> sucursales = new ArrayList<Sucursal>();
		ResultSet rs = null;
		Connection cn = null;
		try {
			cn = this.getConnection();
			String qry = "SELECT suc.Suc_Id, suc.Suc_Nombre FROM [dbo].[Sucursal] suc WHERE SUBSTRING(suc.Suc_Nombre, 1, 3) <> 'ZZ ' ORDER BY  suc.Suc_Nombre";
			this.logger.debug("Ejecutando query SQL: " + qry);
			rs = cn.createStatement().executeQuery(qry);
			while(null != rs && rs.next()) {
				sucursales.add(new Sucursal(rs.getInt("Suc_Id"), rs.getString("Suc_Nombre")));
			}
		} catch (Exception e) {
			this.logger.error("Error tratando de consultar las farmacias: " + e.getMessage(), e);
		} finally {
			if(rs != null) {
				try { rs.close(); } catch(Exception ex) { this.logger.warn("Error tratando de cerrar el ResultSet: " + ex.getMessage()); }
			}
		}
		
		return sucursales;
	}
	
	public Sucursal getSucursal(int idSucursal) {
		Sucursal sucursal = null;
		ResultSet rs = null;
		Connection cn = null;
		try {
			cn = this.getConnection();
			String qry = "SELECT suc.* FROM [dbo].[Sucursal] suc WHERE suc.Suc_Id = " + idSucursal;
			this.logger.debug("Ejecutando query SQL: " + qry);
			rs = cn.createStatement().executeQuery(qry);
			if(null != rs && rs.next()) {
				sucursal = new Sucursal(rs.getInt("Suc_Id"), rs.getString("Suc_Nombre"));
			}
		} catch (Exception e) {
			this.logger.error("Error tratando de consultar las farmacias: " + e.getMessage(), e);
		} finally {
			if(rs != null) {
				try { rs.close(); } catch(Exception ex) { this.logger.warn("Error tratando de cerrar el ResultSet: " + ex.getMessage()); }
			}
		}
		
		return sucursal;
	}
	
	public List<Producto> getProductos() {
		List<Producto> productos = new ArrayList<Producto>();
		ResultSet rs = null;
		Connection cn = null;
		try {
			cn = this.getConnection();
			String qry = "SELECT art.* FROM [dbo].[Articulo] art ORDER BY art.Articulo_Nombre";
			this.logger.debug("Ejecutando query SQL: " + qry);
			rs = cn.createStatement().executeQuery(qry);
			while(null != rs && rs.next()) {
				productos.add(new Producto(rs.getString("Articulo_Id"), rs.getString("Articulo_Nombre"), 1, rs.getDouble("Articulo_Precio1")));
			}
		} catch (Exception e) {
			this.logger.error("Error tratando de consultar los productos: " + e.getMessage(), e);
		} finally {
			if(rs != null) {
				try { rs.close(); } catch(Exception ex) { this.logger.warn("Error tratando de cerrar el ResultSet: " + ex.getMessage()); }
			}
		}
		
		return productos;
	}
	
	public Map<String, Producto> getProductosMap() {
		Map<String, Producto> productos = new HashMap<String, Producto>();
		ResultSet rs = null;
		Connection cn = null;
		try {
			cn = this.getConnection();
			String qry = "SELECT art.* FROM [dbo].[Articulo] art ORDER BY art.Articulo_Nombre";
			this.logger.debug("Ejecutando query SQL: " + qry);
			rs = cn.createStatement().executeQuery(qry);
			while(null != rs && rs.next()) {
				productos.put(rs.getString("Articulo_Id"), new Producto(rs.getString("Articulo_Id"), rs.getString("Articulo_Nombre"), 1, rs.getDouble("Articulo_Precio1")));
			}
		} catch (Exception e) {
			this.logger.error("Error tratando de consultar los productos: " + e.getMessage(), e);
		} finally {
			if(rs != null) {
				try { rs.close(); } catch(Exception ex) { this.logger.warn("Error tratando de cerrar el ResultSet: " + ex.getMessage()); }
			}
		}
		
		return productos;
	}
	
	public List<Producto> getProductos(int pagina, int filasPorPagina, String filtro) {
		List<Producto> productos = new ArrayList<Producto>();
		ResultSet rs = null;
		Connection cn = null;
		try {
			cn = this.getConnection();
			String qry = "DECLARE @Page INT; DECLARE @PageSize INT; ";
			qry += "SET @Page = " + pagina + "; ";
			qry += "SET @PageSize = " + filasPorPagina + "; ";
			
			qry += "SELECT *  FROM (SELECT  ROW_NUMBER() OVER (ORDER BY Articulo_Id asc) as RowNum,* FROM [dbo].[Articulo]) tblArticulo ";
			qry += "WHERE RowNum BETWEEN ( ((@Page - 1) * @PageSize )+ 1) AND @Page*@PageSize";
			
			this.logger.debug("Ejecutando query SQL: " + qry);
			rs = cn.createStatement().executeQuery(qry);
			while(null != rs && rs.next()) {
				productos.add(new Producto(rs.getString("Articulo_Id"), rs.getString("Articulo_Nombre"), 1, rs.getDouble("Articulo_Precio1")));
			}
		} catch (Exception e) {
			this.logger.error("Error tratando de consultar los productos: " + e.getMessage(), e);
		} finally {
			if(rs != null) {
				try { rs.close(); } catch(Exception ex) { this.logger.warn("Error tratando de cerrar el ResultSet: " + ex.getMessage()); }
			}
		}
		
		return productos;
	}
	
	public List<ItemVenta> getItemsVentasProducto(Calendar calEjecucion, Meta meta, GrupoProducto gPrd, List<ExclusionCliente> exclusiones) {
		return this.getItemsVentasProducto(calEjecucion, meta, gPrd, exclusiones, false);
	}
	
	public List<ItemVenta> getItemsVentasProducto(Calendar calEjecucion, Meta meta, GrupoProducto gPrd, List<ExclusionCliente> exclusiones, boolean todosRequeridos) {
		List<ItemVenta> itemsVenta = new ArrayList<ItemVenta>();
		String query = FarmaciaQueriesBuilder.construirQueryConsultaItemsVentas(calEjecucion, meta, gPrd, exclusiones, todosRequeridos).toString();
		ResultSet rs = null;
		Connection cn = null;
		
		try {
			cn = this.getConnection();
			this.logger.debug("Ejecutando query SQL para grupo de productos " + gPrd.getNombre() + ": " + query.toString());
			rs = cn.createStatement().executeQuery(query.toString());
			while(null != rs && rs.next()) {
				try {
					itemsVenta.add(new ItemVenta(rs));
				} catch (Exception e) {
					this.logger.error("Error tratando de construir objeto ItemVenta: " + e, e);
				}
			}
		} catch (SQLException e) {
			this.logger.error("Error tratando de consultar los items de ventas de un detalle de grupo de productos: " + e.getMessage(), e);
		} finally {
			if(rs != null) {
				try { rs.close(); } catch(Exception ex) { this.logger.warn("Error tratando de cerrar el ResultSet: " + ex.getMessage()); }
			}
		}
		
		return itemsVenta;
	}
	
	public int getTotalCantidadVentasProductos(GrupoProducto gPrd, Meta meta, List<ExclusionCliente> exclusiones) {
		int totalCantidad = 0;
		String query = FarmaciaQueriesBuilder.construirQueryConsultaVentas(gPrd, meta, exclusiones).toString();
		ResultSet rs = null;
		Connection cn = null;
		
		try {
			cn = this.getConnection();
			this.logger.debug("Ejecutando query SQL: " + query.toString());
			rs = cn.createStatement().executeQuery(query.toString());
			while(null != rs && rs.next()) {
				int cantidad = 0;
				switch(ModalidadGrupoProducto.fromCodigo(gPrd.getModalidad())) {
				case CUALQUIERA:
					cantidad = new Double(Math.floor(rs.getInt("TOTAL_CANTIDADES") / 1/*{{cantidad definida en detalle grupo de producto neurotropas}}*/)).intValue();
					break;
				case TODOS:
					//Si la modalidad es todos, solo se acumula la cantidad agrupada si la cantidad de tipos de art�culos vendidos 
					//es equivalente a la cantidad especificada en el detalle del grupo de productos
					cantidad = (rs.getInt("TOTAL_ARTICULOS") >= gPrd.getDetalleGrupoProductos().size()? 
							new Double(Math.floor(rs.getInt("TOTAL_CANTIDADES") / 1/*{{cantidad definida en detalle grupo de producto neurotropas}}*/)).intValue(): 0);
					break;
				}
				this.logger.debug("Acumulando cantidad " + cantidad + " de artículos en factura " + rs.getInt("FACTURA_ID"));
				totalCantidad += cantidad;
			}
		} catch(Exception ex) {
			this.logger.error("Error tratando de consultar el total de cantidad de ventas de un producto: " + ex.getMessage(), ex);
		} finally {
			if(rs != null) {
				try { rs.close(); } catch(Exception ex) { this.logger.warn("Error tratando de cerrar el ResultSet: " + ex.getMessage()); }
			}
		}
		
		this.logger.debug("Cantidad total para meta con ID " + meta.getId() + " y nombre " + meta.getNombre() + ": " + totalCantidad);
		return totalCantidad;
	}
	
	public double getTotalPrecioVentasProductos(GrupoProducto gPrd, Meta meta, List<ExclusionCliente> exclusiones) {
		double totalPrecio = 0;
		String query = FarmaciaQueriesBuilder.construirQueryConsultaVentas(gPrd, meta, exclusiones).toString();
		ResultSet rs = null;
		Connection cn = null; 
		
		try {
			cn = this.getConnection();
			this.logger.debug("Ejecutando query SQL: " + query.toString());
			rs = cn.createStatement().executeQuery(query.toString());
			while(null != rs && rs.next()) {
				double precio = 0d; 
				switch(ModalidadGrupoProducto.fromCodigo(gPrd.getModalidad())) {
				case CUALQUIERA:
					precio = rs.getDouble("TOTAL_PRECIO");
					break;
				case TODOS:
					//Si la modalidad es todos, solo se acumula el precio si la cantidad de tipos de artículos vendidos 
					//es equivalente a la cantidad especificada en el detalle del grupo de productos
					precio = (rs.getInt("TOTAL_ARTICULOS") >= gPrd.getDetalleGrupoProductos().size()? rs.getDouble("TOTAL_PRECIO"): 0);
					break;
				}
				this.logger.debug("Acumulando precio " + precio + " de artículos en factura con ID " + rs.getInt("FACTURA_ID") + " y numero " + rs.getInt("FACTURA_NUMERO"));
				totalPrecio += precio;
			}
		} catch(Exception ex) {
			this.logger.error("Error tratando de consultar el total de cantidad de ventas de un producto: " + ex.getMessage(), ex);
		} finally {
			if(rs != null) {
				try { rs.close(); } catch(Exception ex) { this.logger.warn("Error tratando de cerrar el ResultSet: " + ex.getMessage()); }
			}
		}
		
		this.logger.debug("Precio total para meta con ID " + meta.getId() + " y nombre " + meta.getNombre() + ": " + totalPrecio);
		return totalPrecio;
	}
	
	public double getTotalPrecioVentasSucursal(Calendar calEjecucion, Sucursal suc, List<ExclusionCliente> exclusiones) {
		double totalPrecio = 0;
		String query = FarmaciaQueriesBuilder.construirQueryConsultaVentas(calEjecucion, suc, exclusiones).toString();
		ResultSet rs = null;
		Connection cn = null; 
		
		try {
			cn = this.getConnection();
			this.logger.debug("Ejecutando query SQL: " + query.toString());
			rs = cn.createStatement().executeQuery(query.toString());
			while(null != rs && rs.next()) {
				double precio = 0d;
				precio = rs.getDouble("TOTAL_PRECIO");
				totalPrecio += precio;
			}
		} catch(Exception ex) {
			this.logger.error("Error tratando de consultar el total de cantidad de ventas de un producto: " + ex.getMessage(), ex);
		} finally {
			if(rs != null) {
				try { rs.close(); } catch(Exception ex) { this.logger.warn("Error tratando de cerrar el ResultSet: " + ex.getMessage()); }
			}
		}
		
		this.logger.debug("Precio total para sucursal con ID " + suc.getId() + " y nombre " + suc.getNombre() + ": " + totalPrecio);
		return totalPrecio;
	}

	public Properties getConnectionProps() {
		return connectionProps;
	}

	public void setConnectionProps(Properties connectionProps) {
		this.connectionProps = connectionProps;
	}
}
